//
// Created by root on 3/29/23.
//
// Example of parsing JSON to document by parts.

// Using C++11 threads
// Temporarily disable for clang (older version) due to incompatibility with libstdc++
#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1700)) && !defined(__clang__)

#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/writer.h"
#include "rapidjson/ostreamwrapper.h"
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <fstream>

using namespace rapidjson;

template<unsigned parseFlags = kParseDefaultFlags>
class AsyncDocumentParser {
public:
    AsyncDocumentParser(Document& d)
            : stream_(*this)
            , d_(d)
            , parseThread_()
            , mutex_()
            , notEmpty_()
            , complex(false)
            , finish_()
            , completed_()
    {
        // Create and execute thread after all member variables are initialized.
        parseThread_ = std::thread(&AsyncDocumentParser::Parse, this);
    }

    ~AsyncDocumentParser() {
        if (!parseThread_.joinable())
           return;

        {
            std::unique_lock<std::mutex> lock(mutex_);

            // Wait直到stream buffer都读完，或者解析完成
            while (!stream_.Empty() && !completed_)
                finish_.wait(lock);

            // Automatically append '\0' as the terminator in the stream.
            static const char terminator[] = "";
            stream_.src_ = terminator;
            stream_.end_ = terminator + 1;
            notEmpty_.notify_one(); // unblock the AsyncStringStream
        }

        parseThread_.join();
    }

    void ParsePart(const char* buffer, size_t length) {
        std::unique_lock<std::mutex> lock(mutex_);

        // Wait until the buffer is read up (or parsing is completed)
        while (!stream_.Empty() && !completed_)
            finish_.wait(lock);

        // Stop further parsing if the parsing process is completed.
        if (completed_)
            return;

        // Set the buffer to stream and unblock the AsyncStringStream
        stream_.src_ = buffer;
        stream_.end_ = buffer + length;
        notEmpty_.notify_one();
    }
    void ParseEnd(){
        completed_ = true;
    }

    void SetComplex(bool v){
        complex = v;
    }

private:
    void Parse() {
        d_.ParseStream<parseFlags>(stream_);
        if(false == complex){
            std::string hello = d_["hello"].GetString();
            int i = d_["i"].GetInt();
            double pi = d_["pi"].GetDouble();
        }


        if(!d_.IsArray()){
            printf("document is not array type!\n");
            return;
        }

        //success
        for (int i = 0; i < d_.Size(); i++) {
            const rapidjson::Value &object = d_[i];

            std::string id = object["_id"].GetString();
            int index = object["index"].GetInt();
            std::string guid = object["guid"].GetString();
            bool isActive = object["isActive"].GetBool();
            std::string balance = object["balance"].GetString();
            std::string picture = object["picture"].GetString();
            int age = object["age"].GetInt();
            std::string eyeColor = object["eyeColor"].GetString();
            std::string name = object["name"].GetString();
            std::string gender = object["gender"].GetString();
            std::string company = object["company"].GetString();
            std::string email = object["email"].GetString();
            std::string phone = object["phone"].GetString();
            std::string address = object["address"].GetString();
            std::string about = object["about"].GetString();
            std::string registered = object["registered"].GetString();
            double latitude = object["latitude"].GetDouble();
            double longitude = object["longitude"].GetDouble();

            if(object["tags"].IsArray())
            {
                const rapidjson::Value& tagsObj = object["tags"];
                for(int j=0;j < tagsObj.Size(); j++){
                    const rapidjson::Value &tag = tagsObj[j];
                    std::string str = tag.GetString();
                    printf("tag:%s\n",str.c_str());
                }
            }

            if(object["friends"].IsArray()){
                const rapidjson::Value& friendsObj = object["friends"];
                for(int k=0;k < friendsObj.Size(); k++){
                    const rapidjson::Value &tag = friendsObj[k];
                    int id = tag["id"].GetInt();
                    std::string name = tag["name"].GetString();
                    printf("id:%d name:%s\n",id, name.c_str());
                }
            }

            std::string greeting = object["greeting"].GetString();
            std::string favoriteFruit = object["favoriteFruit"].GetString();
        }

        // The stream may not be fully read, notify finish anyway to unblock ParsePart()
        std::unique_lock<std::mutex> lock(mutex_);
        completed_ = true;      // Parsing process is completed
        finish_.notify_one();   // Unblock ParsePart() or destructor if they are waiting.
    }

    struct AsyncStringStream {
        typedef char Ch;

        AsyncStringStream(AsyncDocumentParser& parser) : parser_(parser), src_(), end_(), count_() {}

        char Peek() const {
            std::unique_lock<std::mutex> lock(parser_.mutex_);

            // If nothing in stream, block to wait.
            while (Empty())
                parser_.notEmpty_.wait(lock);

            return *src_;
        }

        char Take() {
            std::unique_lock<std::mutex> lock(parser_.mutex_);

            // If nothing in stream, block to wait.
            while (Empty())
                parser_.notEmpty_.wait(lock);//当不为空时，触发条件

            count_++;
            char c = *src_++;

            // If all stream is read up, notify that the stream is finish.
            if (Empty())
                parser_.finish_.notify_one();

            return c;
        }

        size_t Tell() const { return count_; }

        // Not implemented
        char* PutBegin() { return 0; }
        void Put(char) {}
        void Flush() {}
        size_t PutEnd(char*) { return 0; }

        bool Empty() const { return src_ == end_; }

        AsyncDocumentParser& parser_;
        const char* src_;     //!< Current read position.
        const char* end_;     //!< End of buffer
        size_t count_;        //!< Number of characters taken so far.
    };

    AsyncStringStream stream_;
    Document& d_;

    std::thread parseThread_;
    std::mutex mutex_;
    std::condition_variable notEmpty_;//非空标志
    std::condition_variable finish_; //结束标志
    bool completed_;//解析完成标志
    bool complex;   //解析结构是否复杂
};

//第一种测试情况,json 大小大于100M,json结构中有数组类型object
int testComplexJsonFile(){
    Document dNew;
    {
        AsyncDocumentParser<> parser(dNew);
        parser.SetComplex(true);

        std::ifstream ifs("./generated_12m.json");
        if(!ifs) {
            return -1;
        }

        std::string line;
        std::string result;
        int lineCnt = 0;
        while(getline(ifs, line)){
            result += line;
            lineCnt++;
            if(lineCnt >= 10){
                size_t l = result.length();
                parser.ParsePart(result.c_str(),l);
                lineCnt = 0;
                result = "";
            }
        }

        if(!result.empty()){
            size_t l = result.length();
            parser.ParsePart(result.c_str(),l);
        }

        ifs.close();

        /*FILE* fp = fopen("./generated_12m.json","rb");
        if(NULL == fp){
            printf("json file open failed!\n");
            return -1;
        }

        while (!feof (fp)){
            char readBuffer[1024];
            bzero (readBuffer, 1024);

            size_t nsize = fread(readBuffer, 1, sizeof(readBuffer)-1, fp);
            if(nsize > 0){
                std::string  s = readBuffer;
                size_t l = s.length();
                parser.ParsePart(s.c_str(),l);
            }
        }*/
    }
    return 0;
}

//第二种测试情况,简单json结构
//三个字符串进行拼接
int testSimpleJsonString(){
    Document d;
    {
        AsyncDocumentParser<> parser(d);
        parser.SetComplex(false);

        const char json1[] = " { \"hello\" : \"world\", \"t\" : tr";
        //const char json1[] = " { \"hello\" : \"world\", \"t\" : trX"; // For test parsing error
        const char json2[] = "ue, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.14";
        const char json3[] = "16, \"a\":[1, 2, 3, 4] } ";

        parser.ParsePart(json1, sizeof(json1) - 1);
        parser.ParsePart(json2, sizeof(json2) - 1);
        parser.ParsePart(json3, sizeof(json3) - 1);
    }
    return 0;
}

//第二种测试情况,复杂json结构
//三个字符串进行拼接
int testComplexJsonString(){
    Document d;
    {
        AsyncDocumentParser<> parser(d);
        parser.SetComplex(true);

        const char json4[] = "[\n"
                             "  {\n"
                             "    \"_id\": \"6423b0e2fe214d4763196a33\",\n"
                             "    \"index\": 0,\n"
                             "    \"guid\": \"87e82b1c-1c95-48e8-a459-8a858eed710d\",\n"
                             "    \"isActive\": false,\n"
                             "    \"balance\": \"$2,967.92\",\n"
                             "    \"picture\": \"http://placehold.it/32x32\",\n"
                             "    \"age\": 28,\n"
                             "    \"eyeColor\": \"green\",\n"
                             "    \"name\": \"Marks Montoya\",\n"
                             "    \"gender\": \"male\",\n"
                             "    \"company\": \"CYTRAK\",\n"
                             "    \"email\": \"marksmontoya@cytrak.com\",\n"
                             "    \"phone\": \"+1 (958) 530-3427\",\n"
                             "    \"address\": \"930 Hale Avenue, Ola, New Mexico, 6961\",\n"
                             "    \"about\": \"Dolor irure culpa voluptate dolor et. Pariatur excepteur dolore ut sit tempor reprehenderit ut voluptate aliqua mollit mollit. Lorem commodo aliquip enim sint culpa labore fugiat. Excepteur ad id veniam sit veniam aliquip aliqua do magna est incididunt qui exercitation.\\r\\n\",\n"
                             "    \"registered\": \"2020-12-04T02:13:22 -08:00\",\n"
                             "    \"latitude\": -35.762204,\n"
                             "    \"longitude\": -47.658663,\n"
                             "    \"tags\": [\n"
                             "      \"non\",\n"
                             "      \"elit\",\n"
                             "      \"enim\",\n"
                             "      \"exercitation\",\n"
                             "      \"id\",\n"
                             "      \"do\",\n"
                             "      \"enim\"\n"
                             "    ],";
        const char json5[] = "\"friends\": [\n"
                             "      {\n"
                             "        \"id\": 0,\n"
                             "        \"name\": \"Jordan Dejesus\"\n"
                             "      },\n"
                             "      {\n"
                             "        \"id\": 1,\n"
                             "        \"name\": \"Lawson Dotson\"\n"
                             "      },\n"
                             "      {\n"
                             "        \"id\": 2,\n"
                             "        \"name\": \"Carroll Allison\"\n"
                             "      }\n"
                             "    ],\n"
                             "    \"greeting\": \"Hello, Marks Montoya! You have 8 unread messages.\",\n"
                             "    \"favoriteFruit\": \"banana\"\n"
                             "  },\n"
                             "  {\n"
                             "    \"_id\": \"6423b0e2763d7515bfd5e40c\",\n"
                             "    \"index\": 1,\n"
                             "    \"guid\": \"cbb187c1-bc94-42d2-8c10-5ac72baf943f\",\n"
                             "    \"isActive\": false,\n"
                             "    \"balance\": \"$2,524.69\",\n"
                             "    \"picture\": \"http://placehold.it/32x32\",\n"
                             "    \"age\": 40,\n"
                             "    \"eyeColor\": \"green\",\n"
                             "    \"name\": \"Tania Bernard\",";

        const char json6[] = "    \"gender\": \"female\",\n"
                             "    \"company\": \"ISOTRACK\",\n"
                             "    \"email\": \"taniabernard@isotrack.com\",\n"
                             "    \"phone\": \"+1 (873) 452-2551\",\n"
                             "    \"address\": \"232 Carlton Avenue, Mansfield, Louisiana, 7848\",\n"
                             "    \"about\": \"Irure commodo id quis sint aliquip qui ex est ad. Aliquip ex consectetur veniam dolore occaecat et ad dolore elit do velit ut adipisicing dolore. Sunt enim dolore pariatur consectetur quis laborum ex ullamco. Enim do magna aute velit quis Lorem. Sunt tempor do labore commodo. Tempor pariatur amet adipisicing laborum consectetur sint aute amet irure ut nisi sunt labore.\\r\\n\",\n"
                             "    \"registered\": \"2019-07-15T03:15:01 -08:00\",\n"
                             "    \"latitude\": -22.38927,\n"
                             "    \"longitude\": 105.902289,\n"
                             "    \"tags\": [\n"
                             "      \"ullamco\",\n"
                             "      \"eu\",\n"
                             "      \"qui\",\n"
                             "      \"deserunt\",\n"
                             "      \"reprehenderit\",\n"
                             "      \"amet\",\n"
                             "      \"Lorem\"\n"
                             "    ],\n"
                             "    \"friends\": [\n"
                             "      {\n"
                             "        \"id\": 0,\n"
                             "        \"name\": \"Patricia Chen\"\n"
                             "      },\n"
                             "      {\n"
                             "        \"id\": 1,\n"
                             "        \"name\": \"Mclean Burton\"\n"
                             "      },\n"
                             "      {\n"
                             "        \"id\": 2,\n"
                             "        \"name\": \"Helene Waters\"\n"
                             "      }\n"
                             "    ],\n"
                             "    \"greeting\": \"Hello, Tania Bernard! You have 2 unread messages.\",\n"
                             "    \"favoriteFruit\": \"apple\"\n"
                             "  }\n"
                             "]";
        parser.ParsePart(json4, sizeof(json4) - 1);
        parser.ParsePart(json5, sizeof(json5) - 1);
        parser.ParsePart(json6, sizeof(json6) - 1);
    }
    return 0;
}

#include "rapidjson/filereadstream.h"
int testFileReadStreamJson() {
    rapidjson::Document document;

    //缺点：仅仅支持从文件中读取结果
    //实际场景：用一个大buffer接收数据，或者小buffer接收数据，多次拼装成一个大buffer
    FILE* fp = fopen("./generated.json","rb");
    if(NULL == fp){
        printf("json file open failed!\n");
        return -1;
    }

    char readBuffer[65536];

    rapidjson::FileReadStream is(fp,readBuffer,sizeof(readBuffer));
    document.ParseStream(is);
    if(document.HasParseError()){
        //parse fail
        //printf("parse error: (%d:%lu)%s\n", document.GetParseError(), document.GetErrorOffset(), rapidjson::GetParseError_En(document.GetParseError()));
        return -1;
    }

    if(!document.IsArray()){
        return -2;
    }

    //success
    for (int i = 0; i < document.Size(); i++) {
        const rapidjson::Value &object = document[i];

        std::string id = object["_id"].GetString();
        int index = object["index"].GetInt();
        std::string guid = object["guid"].GetString();
        bool isActive = object["isActive"].GetBool();
        std::string balance = object["balance"].GetString();
        std::string picture = object["picture"].GetString();
        int age = object["age"].GetInt();
        std::string eyeColor = object["eyeColor"].GetString();
        std::string name = object["name"].GetString();
        std::string gender = object["gender"].GetString();
        std::string company = object["company"].GetString();
        std::string email = object["email"].GetString();
        std::string phone = object["phone"].GetString();
        std::string address = object["address"].GetString();
        std::string about = object["about"].GetString();
        std::string registered = object["registered"].GetString();
        double latitude = object["latitude"].GetDouble();
        double longitude = object["longitude"].GetDouble();

        if(object["tags"].IsArray())
        {
            const rapidjson::Value& tagsObj = object["tags"];
            for(int j=0;j < tagsObj.Size(); j++){
                const rapidjson::Value &tag = tagsObj[j];
                std::string str = tag.GetString();
                //printf("tag:%s\n",str.c_str());
            }
        }

        if(object["friends"].IsArray()){
            const rapidjson::Value& friendsObj = object["friends"];
            for(int k=0;k < friendsObj.Size(); k++){
                const rapidjson::Value &tag = friendsObj[k];
                int id = tag["id"].GetInt();
                std::string name = tag["name"].GetString();
                //printf("id:%d name:%s\n",id, name.c_str());
            }
        }

        std::string greeting = object["greeting"].GetString();
        std::string favoriteFruit = object["favoriteFruit"].GetString();
        //printf("id:%s, index:%d, guid:%s\n",id.c_str(), index, guid.c_str());
    }

    //don't forget close file
    fclose(fp);
}

int main() {
    testFileReadStreamJson();//此种方式需要先把json内容写入磁盘，然后再读取，所以不是很好,test result is ok

    //testSimpleJsonString();//简单json结构,test result is ok

    //testComplexJsonString();//复杂json结构,test result is ok

    //testComplexJsonFile();//复杂json结构,从大文件中读取,test reuslt is failed
    return EXIT_SUCCESS;
}

#endif